
#include "xmpplogintask.h"
#include "constants.h"
#include "XmlNode.h"
#include "taskmanager.h"
#include "BASE64Codec.h"
#include <iostream>

namespace tinkle {

XmppLoginTask::XmppLoginTask(const std::string &user, const std::string &pwd,
                             BaseTask *parent)
    : XmppTask(parent), _stanza(""), _user(user), _pwd(pwd), _state(LS_INIT) {
}

XmppLoginTask::~XmppLoginTask() {

}

void XmppLoginTask::ProcessReady() {
    XmlNode *newStanza = NULL;
    std::string content;
    switch (_state) {
    case LS_INIT:
        SendStanza(START_STREAM);
        _state = LS_STREAMINIT_SENT;
        break;
    case LS_STREAMINIT_SENT:
        if (_stanza()->GetName() == STR_STREAM) {
            _state = LS_STREAMINIT_RECVED;
        }
        break;
    case LS_STREAMINIT_RECVED:
        if (_stanza()->GetName() == STR_FEATURES) {
            _state = LS_FEATURES_NOTIFY;
        } else {
            break;
        }
    case LS_FEATURES_NOTIFY:
        newStanza = new XmlNode("starttls");
        (*newStanza)()->DeclNameSpace(NS_TLS, "");
        SendStanza(newStanza);
        _state = LS_TLSINIT_SENT;
        break;
    case LS_TLSINIT_SENT:
        if (_stanza()->GetName() == "proceed" &&
            _stanza()->GetNsDecl("") == NS_TLS) {
            _state = LS_TLSINIT_RECVED;
        } else {
            // some error
            break;
        }
    case LS_TLSINIT_RECVED:
        // Using TLS via openssl
        GetManager()->GetStanzaManager()->Reset();
        GetManager()->GetStanzaManager()->EnableTLS();
        SendStanza(START_STREAM);
        _state = LS_TLSSTREAMINIT_SENT;
        break;
    case LS_TLSSTREAMINIT_SENT:
        if (_stanza()->GetName() == STR_STREAM) {
            _state = LS_TLSSTREAMINIT_RECVED;
        }
        break;
    case LS_TLSSTREAMINIT_RECVED:
        if (_stanza()->GetName() == STR_FEATURES) {
            _state = LS_TLSFEATURES_NOTIFY;
        } else {
            break;
        }
    case LS_TLSFEATURES_NOTIFY:
        // send user and password encoded with Base64
        newStanza = new XmlNode(STR_AUTH);
        (*newStanza)()->DeclNameSpace(NS_SASL, "");
        (*newStanza)()->SetAttrValue(STR_MECHANISM, "PLAIN");
        //std::string content = "\0"+_user+"\0"+_pwd;
        content = '\0'+_user+"@gmail.com"+'\0'+_pwd;        
        (*newStanza)()->SetContent(BASE64Codec::Codec()->Encode(content));
        SendStanza(newStanza);
        _state = LS_SASL_SENT;
        break;
    case LS_SASL_SENT:
        if (_stanza()->GetName() == STR_SUCCESS) {
            _state = LS_SASL_SUCCESS;
        } else {
            break;
        }
    case LS_SASL_SUCCESS:
        // reset the xml parser to parse new xml stream
        GetManager()->GetStanzaManager()->Reset();
        SendStanza(START_STREAM);
        _state = LS_SASLSTREAMINIT_SENT;
        break;
    case LS_SASLSTREAMINIT_SENT:
        if (_stanza()->GetName() == STR_STREAM) {
            _state = LS_SASLSTREAMINIT_RECVED;
        }
        break;
    case LS_SASLSTREAMINIT_RECVED:
        printf ("bind here\n");
        if (_stanza()->GetName() == STR_FEATURES) {
            if (!_stanza()->GetChild(STR_BIND)->IsNull()) {
                // request to bind a resource
                newStanza = new XmlNode(STR_IQ);
                (*newStanza)()->SetAttrValue(STR_TYPE, STR_SET);
                (*newStanza)()->SetAttrValue(STR_ID, GenSessionId());
                ElementPtr _node = (*newStanza)()->AddChild(STR_BIND);
                _node->DeclNameSpace(NS_BIND, "");
                SendStanza(newStanza);
            }
            if (!_stanza()->GetChild(STR_SESSION)->IsNull()) {
                // request to create a new session
                newStanza = new XmlNode(STR_IQ);
                (*newStanza)()->SetAttrValue(STR_TYPE, STR_SET);
                (*newStanza)()->SetAttrValue(STR_ID, GenSessionId());
                ElementPtr _node = (*newStanza)()->AddChild(STR_SESSION);
                _node->DeclNameSpace(NS_SESSION, "");
                SendStanza(newStanza);
            }
        }
        _state = LS_REQUEST_SENT;
        break;
    case LS_REQUEST_SENT:
        if (_stanza()->GetName() == STR_IQ &&
            _stanza()->GetAttrValue(STR_TYPE) == STR_RESULT) {
            ElementPtr _node = _stanza()->GetChild(STR_BIND);
            if (!_node->IsNull()) {
                // store the Jid
                Jid returnJid = Jid(_node->GetChild(STR_JID)->GetContent());
                _jid = returnJid;
                _state = LS_BINDED;
            }
        }
        break;
        case LS_BINDED:
            _state = LS_DONE;
            break;    
    }
}

bool XmppLoginTask::Abort() {
    return true;
}

const Jid & XmppLoginTask::GetJid() const {
    return _jid;
}

bool XmppLoginTask::NextStanza(XmlNode *stanza) {
    if (_state != LS_DONE) {
        // client does not login, so the login task is the only task to
        // handle stanza
        _stanza = *stanza;
        return true;
    }
    return false;
}

}